﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Clustering
{
    public class Kmeans : ClusteringAlgorithm
    {
        private Cluster[] clusters;

        public override Cluster[] CreateClusters(int maxIterations)
        {
            clusters = InitClusters();
            if (clusters == null)
                return null;

            return Continue(maxIterations);


        }

        public Cluster[] Continue(int maxIterations)
        {
            for (int numberOfIterations = 0; numberOfIterations < maxIterations; numberOfIterations++)
            {

                for (int k = 0; k < numberOfClusters; k++)
                {
                    clusters[k].PointsInCluster.Clear();
                }

                //Find clusters
                for (int i = 0; i < points.Count; i++)
                {
                    double minError = SquaredError(clusters[0].Center, points[i]);
                    int minIdx = 0;
                    double error = 0.0;

                    for (int k = 1; k < numberOfClusters; k++)
                    {
                        error = SquaredError(clusters[k].Center, points[i]);
                        if (error < minError)
                            minIdx = k;
                    }

                    clusters[minIdx].PointsInCluster.Add(points[i]);
                }

                for (int k = 0; k < numberOfClusters; k++)
                {
                    clusters[k].CalculateCenter();
                }

                for (int k = 0; k < numberOfClusters; k++)
                {

                    clusters[k].Error = 0;

                    for (int i = 0; i < clusters[k].PointsInCluster.Count; i++)
                    {
                        clusters[k].Error +=
                            SquaredError(clusters[k].PointsInCluster[i], clusters[k].Center);
                    }

                    clusters[k].Error /=
                         clusters[k].PointsInCluster.Count;

                }

            }

            return clusters;

        }

        public Cluster[] InitClusters()
        {
            if (numberOfClusters == 0)
                return null;

            if (points.Count == 0)
                return null;
            Cluster[] clusters = new Cluster[numberOfClusters];

            Random r = new Random();
            int idx1;
            int idx2;

            for (int i = 0; i < numberOfClusters; i++)
            {
                clusters[i] = new Cluster(this.dimension);
                idx1 = r.Next(0, points.Count - 1);
                //idx2 = r.Next(0, points.Count - 1);

                clusters[i].Center = new double[dimension];

                for (int j = 0; j < dimension; j++)
                {
                    //clusters[i].Center[j] = (points[idx1][j] + points[idx2][j]) / 2.0;
                    clusters[i].Center[j] = points[idx1][j];
                    //                    clusters[i].Center[j] = r.Next(100,300);
                }
            }

            return clusters;
        }


        public Cluster[] RepeatInitialClustering(int numberOfStarts, int maxKmeansIterations)
        {
            Cluster[] bestCluster = null;
            double minError = Double.MaxValue;

            for (int i = 0; i < numberOfStarts; i++)
            {
                Cluster[] currentCluster = CreateClusters(maxKmeansIterations);
                if (currentCluster == null)
                    continue;
                double currentError = 0;
                for (int j = 0; j < currentCluster.Length; j++)
                {
                    currentError += currentCluster[j].Error;
                }

                if (currentError < minError)
                {
                    minError = currentError;
                    bestCluster = currentCluster;
                }
            }
            return bestCluster;
        }

        public Cluster[] RepeatContinuationOfClustering(int numberOfStarts, int maxKmeansIterations)
        {
            Cluster[] bestCluster = null;
            double minError = Double.MaxValue;

            Cluster[] startCluster = this.clusters;
            for (int i = 0; i < numberOfStarts; i++)
            {
                this.clusters = startCluster;
                Cluster[] currentCluster = Continue(maxKmeansIterations);
                double currentError = 0;
                for (int j = 0; j < currentCluster.Length; j++)
                {
                    currentError += currentCluster[j].Error;
                }

                if (currentError < minError)
                {
                    minError = currentError;
                    bestCluster = currentCluster;
                }
            }
            return bestCluster;
        }


        private double SquaredError(double[] vector1, double[] vector2)
        {
            //if (vector1.Length != vector2.Length)
            //    throw new Exception("Vector lengths are different");

            double error = 0.0;

            for (int i = 0; i < Math.Min(vector1.Length, vector2.Length); i++)
            {
                error += (vector1[i] - vector2[i]) * (vector1[i] - vector2[i]);
            }

            return error;
        }

        public void RotateClusters()
        {
            Cluster tmp = this.clusters[0];
            for (int i = 0; i < this.clusters.Length - 1; i++)
            {
                this.clusters[i] = this.clusters[i + 1];
            }
            this.clusters[this.clusters.Length - 1] = tmp;
        }
    }
}
